
// =====================================================================================

// =====================================================================================
// Module Name:     PLC模块的控制和通信。
// File Name:       PLC.c
// =====================================================================================
// Version:         V1.00
// Date:            2024-06-20     
//
// =====================================================================================
#include "ProxyUnit_DL645.h"
#include "los_tick.h"
#include <string.h>
#include "Comm.h"
#include "DevModbusDeal.h"
#include "GDDevBase.h"
#include "util.h"
#include "usart_645.h"

// =====================================================================================
// Variable Definitions
// =====================================================================================
#define PLC_SERVICE_STACK_SIZE 0x2000
#define PLC_SERVICE_TASK_NAME "PLC_SERVICE"
#define PLC_SERVICE_TASK_PRIORITY 26
#define PLC_RX_CUMULATIVE_SIZE 512  // 累积缓冲区大小
#define MAX_DEVICES 10
#define PLC_DEBUG 1

#if PLC_DEBUG
u8 g_PlcOldStat = 255;
#endif

u8 RegAddrIndex = 0; 					// 控制搜表过程中上传哪个表地址的变量
u8 AddrSearchComp = 0; 					// 搜表完成标志
u16 ReportAddrIndex = 0;				// 控制主动上送时上传哪个表地址的变量 BIT0表示线路1，BIT1表示线路2...BIT6搜表完成需要主动上送标志，BIT7主动上送完成标志，BIT8主动上送成功标志
BOOLEAN bStartSearchDevAddr = false;	// 区分是否是启动搜表命令，只有搜表命令，才控制 EVENTOUT 来上传所有表地址
Dl645_Public_Type Dl645_Public;

DL645Frame responseModbus;
PLC_CTRL_TYPE PLCCtrl;
PLC_PARA_TYPE PLCPara;
static uint8_t s_rx_cumulative[PLC_RX_CUMULATIVE_SIZE];  // 静态累积缓冲区
static uint16_t s_cumulative_len = 0;  // 当前累积数据长度
static u8 g_localAddr[ADDR_LEN] = {0x66, 0x66, 0x66, 0x66, 0x66, 0x66};
static const uint8_t validPasswords[][3] = { //存储合法的操作密码（实际应从安全存储中获取）
    {0x00, 0x00, 0x00}, // 默认密码1
};
static modbus_device_data_t g_devices[MAX_DEVICES] = {0};
static uint8_t g_cur_report_devices = 0;
static uint8_t g_found_devices = 0;




// =====================================================================================
// Functions
// =====================================================================================

// 声明串口发送函数
extern void usart1_send_data_it(uint8_t *data, uint16_t len);


UINT32 GetSystemRuntimeSeconds() {
    UINT64 ticks = LOS_TickCountGet();
    UINT64 seconds = ticks / LOSCFG_BASE_CORE_TICK_PER_SECOND;

    return (UINT32)seconds;
}


/**
 * @brief 33H数据变换（仅对数据域）
 * @param data 数据域指针
 * @param len 数据长度
 */
static void data_transform(uint8_t *data, uint16_t len) {
    for (uint16_t i = 0; i < len; i++) {
        data[i] += 0x33;
    }
}

/**
 * @brief 33H数据逆变换（仅对数据域）
 * @param data 数据域指针
 * @param len 数据长度
 */
static void data_inverse_transform(uint8_t *data, uint16_t len) {
    for (uint16_t i = 0; i < len; i++) {
        data[i] -= 0x33;
    }
}

/**
 * @brief 计算校验和
 * @param data 数据指针
 * @param len 数据长度
 * @return 校验和
 */
static uint8_t calculate_checksum(const uint8_t *data, uint16_t len) {
    uint32_t sum = 0;
    for (uint16_t i = 0; i < len; i++) {
        sum += data[i];
    }
    return (uint8_t)(sum & 0xFF);
}

/**
 * @brief 将双精度浮点数转换为压缩BCD码
 * @param value 要转换的浮点数值
 * @param bcd_data 输出的BCD数据
 * @param total_digits 总数字位数
 * @param decimal_digits 小数位数
 * 
 * 转换规则:
 * 1. 将浮点数乘以10^decimal_digits，转换为整数
 * 2. 将整数格式化为total_digits位数字(前导零填充)
 * 3. 每两位数字压缩为一个BCD字节
 */
static void double_to_bcd(double value, uint8_t *bcd_data, int total_digits, int decimal_digits) {
    // 计算转换因子
    double factor = 1.0;
    for (int i = 0; i < decimal_digits; i++) {
        factor *= 10.0;
    }
    
    // 1. 将浮点数乘以转换因子，转换为整数
    double scaled_value = value * factor + 0.5;
    
    // 2. 确保值在合理范围内
    double max_value = 1.0;
    for (int i = 0; i < total_digits; i++) {
        max_value *= 10.0;
    }
    max_value -= 1.0;
    
    if (scaled_value > max_value) {
        scaled_value = max_value;
    }
    
    // 3. 转换为数字字符串 (带前导零)
    uint64_t int_value = (uint64_t)scaled_value;
    
    // 生成数字字符串
    char digits[total_digits + 2]; // 增加一点空间
    for (int i = total_digits - 1; i >= 0; i--) {
        digits[i] = '0' + (int_value % 10);
        int_value /= 10;
    }
    digits[total_digits] = '\0';
    
    // 4. 将每两个数字压缩为一个BCD字节
    int bcd_bytes = (total_digits + 1) / 2;
    for (int i = 0; i < bcd_bytes; i++) {
        int idx1 = i * 2;
        int idx2 = i * 2 + 1;
        
        uint8_t high = (idx1 < total_digits) ? (digits[idx1] - '0') : 0;
        uint8_t low = (idx2 < total_digits) ? (digits[idx2] - '0') : 0;
        
        bcd_data[i] = (high << 4) | low;
    }
}

/**
 * @brief 将BCD码转换为浮点数
 * @param bcd_data BCD数据指针
 * @param bcd_len BCD数据长度（字节数）
 * @param decimal_digits 小数位数
 * @return 转换后的浮点数值
 */
static double bcd_to_double(const uint8_t *bcd_data, uint16_t bcd_len, uint8_t decimal_digits) {
    double value = 0.0;
    double factor = 1.0;
    
    // 计算整数部分和小数部分的缩放因子
    for (int i = 0; i < decimal_digits; i++) {
        factor *= 0.1;
    }
    
    // 从最高位开始解析BCD数据
    for (int i = 0; i < bcd_len; i++) {
        uint8_t byte = bcd_data[i];
        
        // 解析高位BCD数字
        uint8_t high = (byte >> 4) & 0x0F;
        value = value * 10.0 + high;
        
        // 解析低位BCD数字
        uint8_t low = byte & 0x0F;
        value = value * 10.0 + low;
    }
    
    // 应用小数位数
    return value * factor;
}

// 验证密码有效性
static bool validatePassword(uint8_t pa, uint8_t p0, uint8_t p1, uint8_t p2) {
    // 96/97级密码不需要验证
    if (pa == PASSWORD_LEVEL_96 || pa == PASSWORD_LEVEL_97) {
        return true;
    }

    // 03/04级密码需要验证
    for (int i = 0; i < sizeof(validPasswords)/sizeof(validPasswords[0]); i++) {
        if (p0 == validPasswords[i][0] && 
            p1 == validPasswords[i][1] && 
            p2 == validPasswords[i][2]) {
            return true;
        }
    }
    return false;
}

// 检查编程键状态（实际需连接硬件）
static bool isProgramKeyActive() {
    // 模拟编程键状态（正常应读取硬件GPIO）
    return true;
}

// 发送失败响应
static void sendFailureResponse(uint8_t* meter_addr, uint8_t control_code, uint8_t error_code) {
    DL645Frame response;
    memset(&response, 0, sizeof(response));
    memcpy(response.meter_addr.addr, meter_addr, ADDR_LEN);
    response.control_code = control_code;
    response.data_len = 1; // 错误码长度
    response.data[0] = error_code;

    send_645_frame(&response, true);
}

// 处理读通信地址请求
static void reqReadAddress(DL645Frame *frame) {
    printf("Handling read address request\n");

    // 构建响应帧
    DL645Frame response;
    memset(&response, 0, sizeof(response));

    // 设置地址(小端模式)
    memcpy(response.meter_addr.addr, g_localAddr, ADDR_LEN);

    response.control_code = CTRL_CODE_READ_ADDR_REP; // 报告通信地址应答
    response.data_len = 6;       // 6字节数据
    
    // 设置回复数据为地址
    for (int i = 0; i < ADDR_LEN; i++) {
        response.data[ADDR_LEN - 1 - i] = response.meter_addr.addr[i];
    }
    
    // 发送响应帧
    send_645_frame(&response, true);
    AddrSearchComp = 1;
}

// 处理读数据请求
static void reqReadData(DL645Frame *frame) {
    if (!PLC_IsRunOK())
    {
        printf("PLC is not run\n");
        return;
    }
    printf("Handling read data request\n");

    // 检查数据长度是否足够包含数据标识符(4字节)
    if (frame->data_len < 4) {
        printf("Invalid read request: data length too short (%u)\n", frame->data_len);
        return;
    }
    u8 return_status;
    u8 responseFlag=true;//应答标志
    // 提取数据标识符(DI0-DI3)
    uint32_t data_id = 0;
    data_id |= frame->data[0];         // DI0
    data_id |= (frame->data[1] << 8);  // DI1
    data_id |= (frame->data[2] << 16); // DI2
    data_id |= (frame->data[3] << 24); // DI3

    // 构建响应帧
    DL645Frame response;
    memset(&response, 0, sizeof(response));

    // 设置表地址（使用请求帧中的地址）
    memcpy(response.meter_addr.addr, frame->meter_addr.addr, ADDR_LEN);

    response.control_code = CTRL_CODE_READ_OK_REP; // 读数据应答
    response.data[0] = frame->data[0];
    response.data[1] = frame->data[1];
    response.data[2] = frame->data[2];
    response.data[3] = frame->data[3];
    uint16_t data_len = 4;

    // 根据数据标识符填充数据
    switch (data_id) {
        case 0x02E60101:    //光伏逆变器1的电压数据块 
		{
			if(invSendFrame.status==Waiting)
			{
				invSendFrame.status=StartSend;
				invSendFrame.function=read;
				invSendFrame.typeItem=VOL_BLOCK;
				invSendFrame.devNum=(Dl645_Public.DI.val&0xff)-1;  
				//new
				u16 size = 0;
				u8 reg_arr[3];
				reg_arr[size++] = vol_a; 
				reg_arr[size++] = vol_b;
				reg_arr[size++] = vol_c;

				invSendFrame.dv =chooseDev(invSendFrame.devNum); //获取逆变器厂家和版本
				return_status = insertSendRegCache_rd(&invSendFrame.dv, reg_arr, size);

                memcpy((DL645Frame *)&responseModbus,frame,14);
                responseFlag=false;

			}
			 
			break;
        }

        case 0x02E60201: //光伏逆变器1的电流数据块
			{
			if(invSendFrame.status==Waiting)
			{
				invSendFrame.status=StartSend;
				invSendFrame.function=read;
				invSendFrame.typeItem=CUR_BLOCK;
				invSendFrame.devNum=(Dl645_Public.DI.val&0xff)-1;
			
				u16 size = 0;
				u8 reg_arr[3];
				reg_arr[size++] = cur_a; 
				reg_arr[size++] = cur_b;
				reg_arr[size++] = cur_c;

				
				invSendFrame.dv =chooseDev(invSendFrame.devNum); //获取逆变器厂家和版本

				return_status=insertSendRegCache_rd(&invSendFrame.dv, reg_arr, size);

                memcpy((DL645Frame *)&responseModbus,frame,14);
                responseFlag=false;
			}
			
			break;
		} 

        case 0x02E60f01: //光伏逆变器1所有变量的数据块
		{
			if(invSendFrame.status==Waiting)
			{
				invSendFrame.status=StartSend;
				invSendFrame.function=read;
				invSendFrame.typeItem=ALL_BLOCK;
				invSendFrame.devNum=(Dl645_Public.DI.val&0xff)-1;
			
				u16 size = 0;
				u8 reg_arr[20];

				reg_arr[size++] =  vol_a;
				reg_arr[size++] =  vol_b;			
				reg_arr[size++] =  vol_c;
				reg_arr[size++] =  cur_a;
				reg_arr[size++] =  cur_b;
				reg_arr[size++] =  cur_c;
				reg_arr[size++] =  p_a;
				reg_arr[size++] =  p_b;
				reg_arr[size++] =  p_c;
				reg_arr[size++] =  p_all;
				reg_arr[size++] =  q_a;
				reg_arr[size++] =  q_b;
				reg_arr[size++] =  q_c;
				reg_arr[size++] =  q_all;
				reg_arr[size++] =  pf_a;
				reg_arr[size++] =  pf_b;
				reg_arr[size++] =  pf_c;
				reg_arr[size++] =  pf_all;

				
				invSendFrame.dv =chooseDev(invSendFrame.devNum); //获取逆变器厂家和版本

				return_status=insertSendRegCache_rd(&invSendFrame.dv, reg_arr, size);

                memcpy((DL645Frame *)&responseModbus,frame,14);
                responseFlag=false;
			}
			break;
		} 


        case 0x04E60101: //光伏逆变器1的额定有功功率
		{
			if(invSendFrame.status==Waiting)
			{
				invSendFrame.status=StartSend;
				invSendFrame.function=read;
				invSendFrame.typeItem=RATA_P;
				invSendFrame.devNum=(Dl645_Public.DI.val&0xff)-1;
			
				u16 size = 0;
				u8 reg_arr[1];

				reg_arr[size++] =  rated_p;

				
				invSendFrame.dv =chooseDev(invSendFrame.devNum); //获取逆变器厂家和版本

				return_status=insertSendRegCache_rd(&invSendFrame.dv, reg_arr, size);

                memcpy((DL645Frame *)&responseModbus,frame,14);
				responseFlag=false;
			}
			break;
		} 

        case 0x04E60201: //光伏逆变器1的额定无功功率
		{
			if(invSendFrame.status==Waiting)
			{
				invSendFrame.status=StartSend;
				invSendFrame.function=read;
				invSendFrame.typeItem=RATA_Q;
				invSendFrame.devNum=(Dl645_Public.DI.val&0xff)-1;
			
				u16 size = 0;
				u8 reg_arr[1];

				reg_arr[size++] =  rated_q;

				
				invSendFrame.dv =chooseDev(invSendFrame.devNum); //获取逆变器厂家和版本

				return_status=insertSendRegCache_rd(&invSendFrame.dv, reg_arr, size);

                memcpy((DL645Frame *)&responseModbus,frame,14);
				responseFlag=false;
			}
			break;
		} 

        case DI_DATE_WEEK: {
            int year = 25;   // 年 (后两位)
            int month = 8;   // 月
            int day = 1;     // 日
            int weak = 5;    // 星期

            uint8_t bcd_data[4] = {0};

            // 添加时间部分 (格式: YYMMDDWW)
            bcd_data[0] = ((year / 10) << 4) | (year % 10);   // YY
            bcd_data[1] = ((month / 10) << 4) | (month % 10); // MM
            bcd_data[2] = ((day / 10) << 4) | (day % 10);     // DD
            bcd_data[3] = ((weak / 10) << 4) | (weak % 10);   // WW
            
            // 复制到响应数据
            memcpy(response.data + 4, bcd_data, 4);
            data_len += 4;
            break;
        }

        case DI_TIME: {
            int hour = 11;     // 时
            int minute = 15;   // 分
            int second = 22;   // 秒

            uint8_t bcd_data[3] = {0};

            // 添加时间部分 (格式: YYMMDDWW)
            bcd_data[0] = ((hour / 10) << 4) | (hour % 10);      // hh
            bcd_data[1] = ((minute / 10) << 4) | (minute % 10);  // mm
            bcd_data[2] = ((second / 10) << 4) | (second % 10);  // ss
            
            // 复制到响应数据
            memcpy(response.data + 4, bcd_data, 3);
            data_len += 3;
            break;
        }

        default:
            printf("Unsupported data identifier for read: 0x%08X\n", data_id);
            sendFailureResponse(response.meter_addr.addr, CTRL_CODE_READ_NOK_REP, ERR_NO_DATA);
            return;
    }

    // 设置数据长度并发送响应
    response.data_len = data_len;
    if(responseFlag==true)
    {
        send_645_frame(&response, true);
    }
}

// 处理读后续数据请求
static void reqReadSequel(DL645Frame *frame) {
    if (!PLC_IsRunOK())
    {
        printf("PLC is not run\n");
        return;
    }
    printf("Handling read sequel request\n");

    // 检查数据长度是否足够包含数据标识符(4字节)
    if (frame->data_len < 4) {
        printf("Invalid read sequel request: data length too short (%u)\n", frame->data_len);
        return;
    }

    // 提取数据标识符(DI0-DI3)
    uint32_t data_id = 0;
    data_id |= frame->data[0];         // DI0
    data_id |= (frame->data[1] << 8);  // DI1
    data_id |= (frame->data[2] << 16); // DI2
    data_id |= (frame->data[3] << 24); // DI3

    // 构建响应帧
    DL645Frame response;
    memset(&response, 0, sizeof(response));

    // 设置表地址（使用请求帧中的地址）
    memcpy(response.meter_addr.addr, frame->meter_addr.addr, ADDR_LEN);

    response.control_code = CTRL_CODE_READ_OK_REP; // 读数据应答
    response.data[0] = frame->data[0];
    response.data[1] = frame->data[1];
    response.data[2] = frame->data[2];
    response.data[3] = frame->data[3];
    uint16_t data_len = 4;

    // 根据数据标识符填充数据
    switch (data_id) {
//        case 

    
        default:
            printf("Unsupported data identifier for read sequel: 0x%08X\n", data_id);
            sendFailureResponse(response.meter_addr.addr, CTRL_CODE_READ_SEQUEL_NOK_REP, ERR_NO_DATA);
            return;
    }

    // 设置数据长度并发送响应
    response.data_len = data_len;
    send_645_frame(&response, true);
}

// 处理写数据请求
static void reqWriteData(DL645Frame *frame) {
    if (!PLC_IsRunOK())
    {
        printf("PLC is not run\n");
        return;
    }
    uint8_t *data = frame->data;
    uint16_t data_len = frame->data_len;

    // 检查最小数据长度 (DI4+PA1+P3+C4=12字节)
    if (data_len < 12) {
#if PLC_DEBUG
        printf("Write request too short: %d bytes\n", data_len);
#endif
        sendFailureResponse(frame->meter_addr.addr, CTRL_CODE_WRITE_NOK_REP, ERR_OTHER);
        return;
    }

    // 解析数据标识 (小端格式)
    uint32_t di = (data[3] << 24) | (data[2] << 16) | (data[1] << 8) | data[0];

    // 解析密码权限和密码
    uint8_t pa = data[4];
    uint8_t p0 = data[5];
    uint8_t p1 = data[6];
    uint8_t p2 = data[7];

    // 解析操作者代码
    uint32_t operator_code = (data[11] << 24) | (data[10] << 16) | 
                             (data[9] << 8) | data[8];

    // 获取实际数据内容（去除头部信息）
    uint8_t *value_data = data + 12;
    uint16_t value_len = data_len - 12;

    // 调试信息
#if PLC_DEBUG
    printf("Write Request:\n");
    printf("  DI: %08X\n", di);
    printf("  Password Level: %02X\n", pa);
    printf("  Password: %02X %02X %02X\n", p0, p1, p2);
    printf("  Operator: %08X\n", operator_code);
    printf("  Data Len: %d\n", value_len);
    printf("  Data: ");
    for (int i = 0; i < value_len; i++) printf("%02X ", value_data[i]);
    printf("\n");
#endif

  

    // 根据数据标识符处理数据
    bool write_success = true;
    double double_value = 0.0;
    int int_value = 0;
    uint32_t uint_value = 0;
    u8 responseFlag=true;
    switch (di) {
        // =================================================================
        // 状态字复位
        // =================================================================
        case DI_RESET_REPORT_STATE_WORD: {
            // 检查数据长度是否正确 (应为12字节)
            if (value_len != 12) {
                write_success = false;
                break;
            }
        
            // 解析状态字 (96位)
#if PLC_DEBUG
            printf("Set reset report status word: \n");
            for (uint8_t i = 0; i < value_len; i ++)
                printf("0x%02X ", value_data[i]);
            printf("\n");
#endif
            break;
        }

        case 0x04e60401: //光伏逆变器1的运行状态
		{

			if(invSendFrame.status==Waiting)
			{

				memcpy(invSendFrame.data,frame->data[5],1);
				invSendFrame.status=StartSend;
				invSendFrame.function=write;
				invSendFrame.typeItem=RunStatus; 
				invSendFrame.devNum=(Dl645_Public.DI.val&0xff)-1;
				
				invSendFrame.dv =chooseDev(invSendFrame.devNum); //获取逆变器厂家和版本

				u16 index = 0;
				Reg_Send_Info reg_arr[1];

				if(invSendFrame.data[0]==gd_run_on)
				{
					reg_arr[index].reg_flag = runStatus_wr_open;
					reg_arr[index].functype = write;
					index++;
				} 
				else if(invSendFrame.data[0]==gd_run_off)
				{
					reg_arr[index].reg_flag = runStatus_wr_close;
					reg_arr[index].functype = write;
					index++;
				}


				
				
				if(!insertSendRegCache_wr(&invSendFrame.dv, reg_arr, index))
				{
					invSendFrame.status=dealFaild;
				}

				memcpy((DL645Frame *)&responseModbus,frame,14);
                responseFlag=false;
			}
			break;
		}

        case 0x04e60501: //光伏逆变器1的有功功率调节
		{

			if(invSendFrame.status==Waiting)
			{
				memcpy(invSendFrame.data,frame->data[5],p_len);
				invSendFrame.status=StartSend;
				invSendFrame.function=write;
				invSendFrame.typeItem=P_SET; 
				invSendFrame.devNum=(Dl645_Public.DI.val&0xff)-1;
				
				invSendFrame.dv =chooseDev(invSendFrame.devNum); //获取逆变器厂家和版本
				

				u16 index = 0;
				Reg_Send_Info reg_arr[4];
				

				reg_arr[index].reg_flag = p_set_model;
				reg_arr[index].functype = write;
				index++;
				reg_arr[index].reg_flag = p_set_num;
				reg_arr[index].functype = write;
				index++;

				if(!insertSendRegCache_wr(&invSendFrame.dv, reg_arr, index))
				{
					invSendFrame.status=dealFaild;
				}

				memcpy((DL645Frame *)&responseModbus,frame,14);
                
			}

            responseFlag=false;
			break;
		}

        case 0x04e60601: //光伏逆变器1的无功功率调节
		{

			if(invSendFrame.status==Waiting)
			{

				memcpy(invSendFrame.data,frame->data[5],q_len);
				invSendFrame.status=StartSend;
				invSendFrame.function=write;
				invSendFrame.typeItem=Q_SET; 
				invSendFrame.devNum=(Dl645_Public.DI.val&0xff)-1;
				
				invSendFrame.dv =chooseDev(invSendFrame.devNum); //获取逆变器厂家和版本

				u16 index = 0;
				Reg_Send_Info reg_arr[4];
				
				reg_arr[index].reg_flag= q_set_model;
				reg_arr[index].functype = write;
				index++;

				reg_arr[index].reg_flag= q_set_num;
				reg_arr[index].functype = write;
				index++;
				
				if(!insertSendRegCache_wr(&invSendFrame.dv, reg_arr, index))
				{
					invSendFrame.status=dealFaild;
				}

				memcpy((DL645Frame *)&responseModbus,frame,14);
                responseFlag=false;
			}
		
            responseFlag=false;
			break;
		}	

        default:
            printf("Unsupported data identifier for write: 0x%08X\n", di);
            sendFailureResponse(frame->meter_addr.addr, CTRL_CODE_WRITE_NOK_REP, ERR_OTHER);
            return;
    }
    
    if (!write_success) {
        printf("Invalid data length for DI 0x%08X: value_len = %d\n", di, value_len);
        sendFailureResponse(frame->meter_addr.addr, CTRL_CODE_WRITE_NOK_REP, ERR_OTHER);
        return;
    }
    
    // 构建写正常响应
    DL645Frame response;
    memset(&response, 0, sizeof(response));
    memcpy(response.meter_addr.addr, frame->meter_addr.addr, ADDR_LEN);
    response.control_code = CTRL_CODE_WRITE_OK_REP;
    response.data_len = 0; // 正常响应无数据域
    
    // 发送响应
    if(responseFlag==true)
    {
        send_645_frame(&response, true);
    }
}

static void reqWriteAddress(DL645Frame *frame) {
    printf("Handling write address request\n");
    // TODO: 实现地址设置处理逻辑
}

static void reqBroadcastSync(DL645Frame *frame) {
    printf("Handling broadcast time sync\n");
    // TODO: 实现时间同步逻辑
}

void process_dl645_frame(DL645Frame *frame) {
    if (!frame) return;

    switch (frame->control_code) {
        case CTRL_CODE_REPORT_ADDRESS_REQ: // 0x13 报告通信地址
            reqReadAddress(frame);
            break;

        case CTRL_CODE_READ_REQ:           // 0x11 读数据
            reqReadData(frame);
            break;

        case CTRL_CODE_READ_SEQUEL_REQ:    // 0x12 读后续数据
            reqReadSequel(frame);
            break;

        case CTRL_CODE_WRITE_REQ:          // 0x14 写数据
            reqWriteData(frame);
            break;

        case CTRL_CODE_WRITE_ADDR_REQ:     // 0x15 设置通信地址
            reqWriteAddress(frame);
            break;

        case CTRL_CODE_BROADCAST_SYNC_REQ: // 0x08 广播校时
            reqBroadcastSync(frame);
            break;

        default:
            printf("Unhandled control code: 0x%02X\n", frame->control_code);
            break;
    }
}


/**
 * @brief 发送DL645协议帧应答帧
 * @param frame 帧结构体指针
 * @return 0表示成功，-1表示参数错误
 */
int respone_645_frame(const DL645Frame *frame, bool enable_data_transform) {
    // 参数校验
    if (!frame || frame->data_len > MAX_FRAME_LEN) {
        printf("Invalid frame parameters\n");
        return -1;
    }
    
    // 帧结构: [0x68][地址域][0x68][控制码][数据长度][数据域][校验和][0x16]
    const uint16_t addr_start = 1;
    const uint16_t second_start = addr_start + ADDR_LEN;
    const uint16_t ctrl_start = second_start + 1;
    const uint16_t len_start = ctrl_start + 1;
    const uint16_t data_start = len_start + 1;
    const uint16_t cs_start = data_start + frame->data_len;
    const uint16_t frame_len = cs_start + 2;
    
    // 分配临时缓冲区
    uint8_t frame_buf[frame_len];
    memset(frame_buf, 0, frame_len);

    // 1. 构建帧
    frame_buf[0] = FRAME_START; // 起始符

    // 地址域（小端顺序：addr[5]在前，addr[0]在后）
    for (int i = 0; i < ADDR_LEN; i++) {
        frame_buf[addr_start + i] = frame->meter_addr.addr[ADDR_LEN - 1 - i];
    }

    frame_buf[second_start] = FRAME_START; // 第二个起始符
    frame_buf[ctrl_start] = frame->control_code|0x80; // 控制码
    frame_buf[len_start] = frame->data_len;     // 数据长度
    
    // 复制原始数据域
    if (frame->data_len > 0) {
        memcpy(&frame_buf[data_start], frame->data, frame->data_len);
    }

    // 2. 对数据域进行33H变换
    if (enable_data_transform && frame->data_len > 0) {
        data_transform(&frame_buf[data_start], frame->data_len);
    }

    // 3. 计算校验和（从第一个0x68开始到数据域结束）
    uint8_t cs = calculate_checksum(frame_buf, cs_start);
    frame_buf[cs_start] = cs; // 设置校验和

    frame_buf[cs_start + 1] = FRAME_END; // 结束符

    // 调试输出
    #if PLC_DEBUG
    printf("respone 645 frame: ");
    for(int i = 0; i < frame_len; i++) {
        printf("%02X ", frame_buf[i]);
    }
    printf("\n");
    #endif

    // 4. 通过串口发送
    usart1_send_data_it(frame_buf, frame_len);
    return 0;
}


/**
 * @brief 发送DL645协议帧
 * @param frame 帧结构体指针
 * @return 0表示成功，-1表示参数错误
 */
int send_645_frame(const DL645Frame *frame, bool enable_data_transform) {
    // 参数校验
    if (!frame || frame->data_len > MAX_FRAME_LEN) {
        printf("Invalid frame parameters\n");
        return -1;
    }
    
    // 帧结构: [0x68][地址域][0x68][控制码][数据长度][数据域][校验和][0x16]
    const uint16_t addr_start = 1;
    const uint16_t second_start = addr_start + ADDR_LEN;
    const uint16_t ctrl_start = second_start + 1;
    const uint16_t len_start = ctrl_start + 1;
    const uint16_t data_start = len_start + 1;
    const uint16_t cs_start = data_start + frame->data_len;
    const uint16_t frame_len = cs_start + 2;
    
    // 分配临时缓冲区
    uint8_t frame_buf[frame_len];
    memset(frame_buf, 0, frame_len);

    // 1. 构建帧
    frame_buf[0] = FRAME_START; // 起始符

    // 地址域（小端顺序：addr[5]在前，addr[0]在后）
    for (int i = 0; i < ADDR_LEN; i++) {
        frame_buf[addr_start + i] = frame->meter_addr.addr[ADDR_LEN - 1 - i];
    }

    frame_buf[second_start] = FRAME_START; // 第二个起始符
    frame_buf[ctrl_start] = frame->control_code; // 控制码
    frame_buf[len_start] = frame->data_len;     // 数据长度
    
    // 复制原始数据域
    if (frame->data_len > 0) {
        memcpy(&frame_buf[data_start], frame->data, frame->data_len);
    }

    // 2. 对数据域进行33H变换
    if (enable_data_transform && frame->data_len > 0) {
        data_transform(&frame_buf[data_start], frame->data_len);
    }

    // 3. 计算校验和（从第一个0x68开始到数据域结束）
    uint8_t cs = calculate_checksum(frame_buf, cs_start);
    frame_buf[cs_start] = cs; // 设置校验和

    frame_buf[cs_start + 1] = FRAME_END; // 结束符

    // 调试输出
    #if PLC_DEBUG
    printf("Sending 645 frame: ");
    for(int i = 0; i < frame_len; i++) {
        printf("%02X ", frame_buf[i]);
    }
    printf("\n");
    #endif

    // 4. 通过串口发送
    usart1_send_data_it(frame_buf, frame_len);
    return 0;
}

// 内部解析函数
static int parse_frame_data(const uint8_t *frame_data, uint16_t len, DL645Frame *frame) {
    // 注意：这里传入的frame_data已经是累积缓冲区的一个片段
    uint8_t tmp_buf[MAX_FRAME_LEN];
    memcpy(tmp_buf, frame_data, len);

    // 定位各个字段
    // 数据域开始位置：10（索引从0: 起始符0 + 地址6 + 第二个起始符7 + 控制码8 + 数据长度9 -> 数据域10）
    const uint16_t data_start = 10;
    uint8_t data_len = tmp_buf[9]; // 数据域长度
    uint16_t cs_pos = data_start + data_len; // 校验和位置

    // 计算校验和（从0到校验和之前）
    uint8_t calc_cs = calculate_checksum(tmp_buf, cs_pos);
    if (calc_cs != tmp_buf[cs_pos]) {
        return -1; // 校验失败
    }

    // 对数据域进行33H逆变换
    if (data_len > 0) {
        data_inverse_transform(&tmp_buf[data_start], data_len);
    }

    // 解析有效数据
    frame->control_code = tmp_buf[8]; // 控制码
    frame->data_len = data_len;
    
    // 地址域转换（协议要求地址域传输顺序即为低字节在前）
    for (int i = 0; i < ADDR_LEN; i++) {
        frame->meter_addr.addr[ADDR_LEN - 1 - i] = tmp_buf[1 + i]; // 索引1-6为地址域
    }

    // 复制数据域（已经是逆变换后的原始数据）
    if (data_len > 0) {
        memcpy(frame->data, &tmp_buf[data_start], data_len);
    }

    PLCCtrl.ComlinkTM = GetSystemRuntimeSeconds();
    #if PLC_DEBUG
    printf("Receive:");
    for (int i = 0; i < len; i++) {
        printf("%02X ", tmp_buf[i]);
    }
    printf("\n");
    #endif
    return 0; // 成功
}


/**
 * @brief 接收并解析DL645帧
 * @param frame 用于存储解析后的帧结构体
 * @return 状态码：0成功，-1校验失败，-2没有完整帧
 */
int receive_645_frame(DL645Frame *frame) {
    // 1. 从串口读取新数据并追加到累积缓冲区
    uint16_t new_data_len = usart1_get_rx_data_len();
    if (new_data_len > 0) {
        // 确保不超过缓冲区容量
        if (s_cumulative_len + new_data_len > PLC_RX_CUMULATIVE_SIZE) {
            // 缓冲区溢出处理：保留尾部的200字节
            uint16_t keep_len = 200;
            if (s_cumulative_len > keep_len) {
                uint16_t remove_len = s_cumulative_len - keep_len;
                memmove(s_rx_cumulative, s_rx_cumulative + remove_len, keep_len);
                s_cumulative_len = keep_len;
            }
            // 如果保留后空间还是不够，只读取能放下的部分
            uint16_t available = PLC_RX_CUMULATIVE_SIZE - s_cumulative_len;
            new_data_len = (new_data_len > available) ? available : new_data_len;
        }
        
        usart1_read_rx_data(&s_rx_cumulative[s_cumulative_len], new_data_len);
        s_cumulative_len += new_data_len;
    }

    // 2. 在累积缓冲区中查找完整帧
    uint16_t frame_start = 0;
    while (frame_start < s_cumulative_len) {
        // 查找第一个起始符0x68
        if (s_rx_cumulative[frame_start] != FRAME_START) {
            frame_start++;
            continue;
        }

        // 检查剩余长度是否至少能包含最小帧（12字节）
        if (s_cumulative_len - frame_start < 12) {
            break; // 数据不足，等待更多
        }

        // 检查第二个起始符0x68（在7字节之后）
        if (s_rx_cumulative[frame_start + 7] != FRAME_START) {
            frame_start++; // 不符合帧结构，继续查找
            continue;
        }

        // 提取数据域长度（第9个字节）
        uint8_t data_len_field = s_rx_cumulative[frame_start + 9];
        uint16_t expected_frame_len = 12 + data_len_field;

        // 检查帧长度是否合法
        if (expected_frame_len > MAX_FRAME_LEN) {
            // 帧过长，跳过整个帧起始符
            frame_start++;
            continue;
        }

        // 检查是否收到完整帧
        if (s_cumulative_len - frame_start < expected_frame_len) {
            break; // 数据不足，等待更多
        }

        // 检查结束符0x16（在帧的最后一个字节）
        uint16_t frame_end_index = frame_start + expected_frame_len - 1;
        if (s_rx_cumulative[frame_end_index] != FRAME_END) {
            frame_start++; // 结束符错误，跳过当前帧头
            continue;
        }

        // 3. 调用帧解析函数
        int result = parse_frame_data(&s_rx_cumulative[frame_start], expected_frame_len, frame);

        // 4. 从累积缓冲区移除已处理数据
        uint16_t remaining = s_cumulative_len - (frame_start + expected_frame_len);
        if (remaining > 0) {
            memmove(s_rx_cumulative, s_rx_cumulative + frame_start + expected_frame_len, remaining);
        }
        s_cumulative_len = remaining;

        return result; // 返回解析结果
    }

    return -2; // 没有完整帧
}


// 拉高事件上报电平
void PLC_ActiveReport(modbus_device_data_t *devices, uint8_t found_devices) {
    if (!PLC_IsRunOK())
        return;

    memcpy(g_devices, devices, sizeof(modbus_device_data_t) * MAX_DEVICES);
    g_found_devices = found_devices;
    printf("Active_Report: GPIO_PLC_EVENTOUT_ON, need to report event\n");
    GPIO_PLC_EVENTOUT_ON;
    PLCCtrl.EvtTM = GetSystemRuntimeSeconds(); // 记录上报时间
}


// =====================================================================================
// 模块串口通信init
// =====================================================================================
void PLC_Port_Init(u32 dwBaudrateNo)
{
    usart1_645_init(dwBaudrateNo);
}


// =====================================================================================
// 模块IO交互init
// =====================================================================================
void PLC_IO_Init(void)
{
    gpio_init_type gpio_init_struct;

    gpio_init_struct.gpio_pins = GPIO_PLC_RST_PIN;              // RST-PLC
    gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
    gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT;
    gpio_init_struct.gpio_out_type	= GPIO_OUTPUT_OPEN_DRAIN;
    gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_MODERATE;
    gpio_init(GPIO_PLC_RST_PORT, &gpio_init_struct);
    GPIO_PLC_RST_ON;                                            // 输出高电平，常态为高阻，低电平复位通讯模块

    gpio_init_struct.gpio_pins = GPIO_PLC_EVENTOUT_PIN;         // EVENTOUT-PLC
    gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT;
    gpio_init(GPIO_PLC_EVENTOUT_PORT, &gpio_init_struct);
    GPIO_PLC_EVENTOUT_OFF;                                      // 默认低电平，有事件需要上送配置为高电平

    //PLC模块电源控制(可能没有电源控制)
    gpio_init_struct.gpio_pins = GPIO_PLCPOWER_PIN;
    gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT;
    gpio_init_struct.gpio_out_type	= GPIO_OUTPUT_PUSH_PULL;    // 推挽输出
    gpio_init(GPIO_PLCPOWER_PORT, &gpio_init_struct);
    GPIO_PLCPOWER_OFF;                                          // 端口初始化完成后关闭电源输出
}


// =====================================================================================
// 模块参数init
// =====================================================================================	
void PLC_Para_Init(void)
{
    PLCPara.keepLive = PLC_KEEPLIVE_SESC_DEFAULT;
    PLCPara.bps = PLC_BR_DEFAULT;
}

// =====================================================================================
// 模块变量init
// =====================================================================================	
void PLC_Ctrl_Init(void)
{
    memset(&PLCCtrl, 0, sizeof(PLCCtrl));
}


// =====================================================================================
// 函数名 : PLC_Init
// 描  述 : PLC模块初始化
// 输  入 : None
// 输  出 : None
// 返回值 : None
// =====================================================================================
void PLC_Init(void)
{
    PLC_Para_Init();
    PLC_IO_Init();
    PLC_Port_Init(PLCPara.bps);
    PLC_Ctrl_Init();
    PLCCtrl.stat = PLC_STAT_PWROFF;
    PLCCtrl.OpStep = PLC_OPSTEP_SHUTOFF;
    uint32_t sysTSec = GetSystemRuntimeSeconds();
    PLCCtrl.nextPwrOnTM = sysTSec?sysTSec:1;    // 等待开机
    printf("PLCCtrl.nextPwrOnTM = %u\n", PLCCtrl.nextPwrOnTM);
    PLCCtrl.IsAllowPwrOn = false;

    AddrSearchComp = 0;
}


// =====================================================================================
// 描  述 : 复位模块
// 输  入 : None
// 输  出 : None
// 返回值 : None
// =====================================================================================
void PLC_Reset(void)
{
    GPIO_PLCPOWER_ON;                               // 电源打开
    GPIO_PLC_EVENTOUT_OFF;
    GPIO_PLC_RST_ON;
    PLCCtrl.ticker = TickerStart();
    PLCCtrl.OpStep = PLC_OPSTEP_RESET;
    PLCCtrl.stat = PLC_STAT_PWRON;
    PLCCtrl.rstCnt++;
    PLCCtrl.rstTM = GetSystemRuntimeSeconds();
    PLCCtrl.ComlinkTM = PLCCtrl.rstTM;
    AddrSearchComp = 0;
}


// =====================================================================================
// 模块关闭
// sec2PwrOn:离下次上电的秒钟数，为0代表永久性关停
// =====================================================================================
void PLC_TurnOff(u32 sec2PwrOn)
{
    // 电源关闭
    GPIO_PLCPOWER_OFF;
    // 其他相连的管脚都输出L
    GPIO_PLC_EVENTOUT_OFF;
    GPIO_PLC_RST_ON;

    PLCCtrl.pwrOffTM = GetSystemRuntimeSeconds();
    PLCCtrl.stat = PLC_STAT_PWROFF;
    PLCCtrl.OpStep = PLC_OPSTEP_SHUTOFF;
    if (sec2PwrOn)
    {// 需要再次上电
        PLCCtrl.nextPwrOnTM = PLCCtrl.pwrOffTM + sec2PwrOn;
    }
    else
    {// 暂时不再上电
        PLCCtrl.nextPwrOnTM = 0;
    }
}

// =====================================================================================
// PLC是否已经正常工作
// =====================================================================================
static bool PLC_IsRunOK(void)
{
    if (!AddrSearchComp)
        return false;
    if (PLCCtrl.stat == PLC_STAT_PWROFF)
        return false;
    if (PLCCtrl.OpStep != PLC_OPSTEP_NORMAL)
        return false;

    return true;
}


// =====================================================================================
// 描  述 : PLC上电运行后的常规检查
// 输  入 : None
// 输  出 : None
// 返回值 : None
// =====================================================================================
void PLC_RunCheck(void)
{
    // EVENTOUT超时关闭逻辑（拉高电平后，若60s内没有收到主站状态字请求则关闭）
    if (PLCCtrl.EvtTM && (GetSystemRuntimeSeconds() - PLCCtrl.EvtTM >= 60)) {
        GPIO_PLC_EVENTOUT_OFF;
        PLCCtrl.EvtTM = 0;
        g_cur_report_devices = 0;
        g_found_devices = 0;
        printf("Active_Report: GPIO_PLC_EVENTOUT_OFF, timeout\n");
    }
    
    // 长时间未收到模块的通讯数据，则重启模块
    if (PLCPara.keepLive)
    {// 开启保活检查的情况下
        if (GetSystemRuntimeSeconds() - PLCCtrl.ComlinkTM > PLCPara.keepLive)
        {
            if (PLCCtrl.rstCnt >= 3)
            {// 重启次数太多
                printf("Active_Report: Timeout and restart too many times. Power off and restart after 60 second.\n");
                PLC_TurnOff(60);           // 尝试彻底  掉电一次
                PLCCtrl.rstCnt = 0;
            }
            else
            {
                printf("Active_Report: Module has not received data for long time, restarting, %u time.\n", PLCCtrl.rstCnt + 1);
                PLC_Reset();
            }
        }
    }

    // 搜表成功与否
    if (AddrSearchComp)
    {// 成功
        PLCCtrl.rstCnt = 0;
    }
}


// =====================================================================================
// 命令操作的执行
// 正常情况下这些命令都为一次性的，如果不是则要特殊说明
// =====================================================================================
void PLC_CMDDo(void)
{
    if (PLCCtrl.OpFlag.BitsField.NeedPwrOff)
    {
        PLCCtrl.OpFlag.BitsField.NeedPwrOff = 0;
        PLC_TurnOff(0);
    }

    // 重启模块
    if (PLCCtrl.OpFlag.BitsField.NeedReset)
    {
        PLCCtrl.OpFlag.BitsField.NeedReset = 0;
        if (PLCCtrl.IsAllowPwrOn)
        {
            PLC_Reset();
        }
    }
}


// =====================================================================================
// 模块状态机控制与执行
// =====================================================================================
void PLC_StepDo(void)
{
    // 响应对功耗控制模块的需求  IsAllowPwrOn状态为持续性
    // 不允许工作，需关闭模块。允许打开模块，还需要看模块自己的状态机才行，不能一味的重新开机
    if (PLCCtrl.IsAllowPwrOn == false)	
    {
        if (PLCCtrl.stat != PLC_STAT_PWROFF)
        {
            PLC_TurnOff(2);
        }
    }

    // 跑状态机
    switch (PLCCtrl.OpStep)
    {
        // 关机阶段
        case PLC_OPSTEP_SHUTOFF:
            if (PLCCtrl.IsAllowPwrOn && PLCCtrl.nextPwrOnTM)
            {
                PLCCtrl.OpStep = PLC_OPSTEP_WAITPWRON;
            }
            break;

        // 停电后等待再次上电
        case PLC_OPSTEP_WAITPWRON:
            if (GetSystemRuntimeSeconds() > PLCCtrl.nextPwrOnTM)
            {
                PLC_Reset();
            }
            break;

        // 复位阶段(指复位信号有效期间)
        case PLC_OPSTEP_RESET:
            if (TickerOut(PLCCtrl.ticker, MS2TICKS(220)))
            {
                GPIO_PLC_RST_OFF;                       // 复位管腿拉高，停止输出复位信号
                PLCCtrl.OpStep = PLC_OPSTEP_INIT;
            }
            break;

        // 复位后的初始化
        // 载波模块的初始化，其实就是模块能从我们这里搜到表地址，这个过程不是我们主动的，
        // 我们只能被动的等待或者被动的通过evtout进行配合
        case PLC_OPSTEP_INIT:
            PLCCtrl.OpStep = PLC_OPSTEP_NORMAL;
            break;

        // 正常透传阶段
        case PLC_OPSTEP_NORMAL:
            PLC_RunCheck();
            break;

        // 其他状态均认为异常
        default:
            PLC_Reset();
            break;
    }
}

// =====================================================================================
// 处理通信
// =====================================================================================
void PLC_RxTxDo(void) {
    DL645Frame frame;
    
    int result = receive_645_frame(&frame);
    
    if (result == 0) {

        // 处理帧内容
        process_dl645_frame(&frame);
    } 
    else if (result == -2) {
        // 没有完整帧 - 正常情况，无需处理
    }
    else {
        printf("Frame error: %d\n", result);
    }
}



// =====================================================================================
// 主控制任务
// =====================================================================================
void* PlcServiceEntry(void* arg)
{
    UINT64 curSec;
    
    PLC_Init();
    PLCCtrl.IsAllowPwrOn = true;
    while(1)
    {
        PLC_CMDDo();
        PLC_StepDo();                   // 模块工作状态机维护
        if (PLCCtrl.stat != PLC_STAT_PWROFF)
        {
            PLC_RxTxDo();
        }
#if PLC_DEBUG
        if (g_PlcOldStat != PLCCtrl.OpStep)
        {
            g_PlcOldStat = PLCCtrl.OpStep;
            printf("PLCCtrl.OpStep:%d\n", PLCCtrl.OpStep);
        }
#endif
        LOS_TaskDelay(30);
    }
}


void PLC_Service(void)
{
    UINT32 ret;
    UINT32 taskID1;
    TSK_INIT_PARAM_S stTask = {0};

    printf("PLC_Service start...\n");
    stTask.pfnTaskEntry = (TSK_ENTRY_FUNC)PlcServiceEntry;
    stTask.uwStackSize = PLC_SERVICE_STACK_SIZE;
    stTask.pcName = PLC_SERVICE_TASK_NAME;
    stTask.usTaskPrio = PLC_SERVICE_TASK_PRIORITY;
    ret = LOS_TaskCreate(&taskID1, &stTask);
    if (ret != LOS_OK) {
        printf("PLC_Service failed\n");
    }
}


SYS_RUN(PLC_Service);

